﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using Noris.Tools.TraceVisualiser.Interface;
using Noris.Tools.TraceVisualiser.Support;
using Noris.Tools.TraceVisualiser.Components;

namespace Noris.Tools.TraceVisualiser.TraceData
{
    // zde jsou obsaženy definice interfaces, které umožňují libovolně rozšířit formou Pluginů možnosti vizualizeru.
    #region ITraceReader : načítač položek ze vstupního trace souboru
    /// <summary>
    /// ITraceReader : načítač položek ze vstupního trace souboru
    /// </summary>
    public interface ITraceReader : IPlugin, IDisposable
    {
        /// <summary>
        /// Zde reader vrací výraz, který se použije v dialogu při výběru vstupních trace soubor.
        /// Různé readery mají různé vstupní soubory s různou příponou, a my nechceme žádný reader opomenout.
        /// Reader může umět zpracovávat více různých koncovek, proto se používá string[], každá položka obsahuje jeden filtr.
        /// Každý jednotlivý filtr je ve tvaru: "Textový popisek|filtr", například: "Aplikační trace Green (*.csv)|*csv".
        /// </summary>
        string[] FileOpenFilters { get; }
        /// <summary>
        /// <para>
        /// Metoda má za úkol podívat se na název souboru,
        /// a vyjádřit se k tomu, zda se bude chtít podívat i dovnitř souboru (tím, že nastaví args.CheckContent = true).
        /// Metoda by měla (pokud se tento TraceReader dokáže s názvem souboru vypořádat) rozebrat název souboru na jméno serveru, a oddělit i pořadové číslo z názvu.
        /// Metoda absolutně nemá za úkol otevírat soubor, pracuje jen s názvem.
        /// Pokud se metoda bude chtít podívat do souboru, nastaví args.CheckContent = true, a za chvíli se vyvolá další metoda: this.CheckContent().
        /// </para>
        /// <para>
        /// V této metodě by bylo možno deklarovat vlastnosti serveru a režimy zobrazování, ukládat je do args (a tamodtud pak do serveru při jeho prvním zakládání)
        /// </para>
        /// </summary>
        /// <param name="args">Data o souboru (název a výsledky této metody)</param>
        /// <returns></returns>
        void CheckFileName(TraceReaderCheckNameArgs args);
        /// <summary>
        /// Metoda je volaná tehdy, když tento reader se přihlásil ke čtení záhlaví souboru (v metodě CheckFileName, nastavil args.CheckContent = true).
        /// Tato metoda nemá za úkol číst data, ale podívat se dovnitř, a usoudit, zda my chceme tento soubor zpracovávat.
        /// Soubor je otevře, a je k němu přístup přes argument
        /// </summary>
        /// <param name="args"></param>
        void CheckContent(TraceReaderCheckContentArgs args);
		/// <summary>
		/// Metoda je volaná pro zpracování celého obsahu dat.
		/// Veškerou podporu má v argumentu TraceReaderReadItemArgs args.
		/// Metoda si může libovolně zpracovat vstupující soubor 
		/// a vytvářet z něj data ve formě jednotlivých záznamů typu ITraceItem.
		/// Tyto záznamy předává ke zpracování do datového systému pomocí volání metody argumentu ProcessItem(ITraceItem item).
		/// Metoda takto může vynechat některé věty (nedávat je ke zpracování), anebo některé věty zpracovat více způsoby
		/// (například z jedné věty trace vytvoří dvě položky: jednu ktertá představuje aktivitu uživatele, a druhou která popisuje stav paměti).
		/// Tím, že zpracování probíhá v jedné metodě, si reader může udržovat kontinuální povědomí o načítaných datech, 
		/// a například o stavu paměti posílat položky při změně hodnoty (není třeba posílat 1000 x jednu hodnotu stavu paměti).
		/// </summary>
		/// <param name="args"></param>
		void ProcessContent(TraceReaderProcessContentArgs args);
        /// <summary>
        /// Tato metoda má za úkol vytvořit a vrátit logickou událost na základě fyzických dat načtených z trace
        /// (před malinkou chvilkou je objekt osobně načetl a vrátil v metodě ReadItem()).
        /// Pokud se vrátí null, pak tato položka trace nemá být zakládána do evidence.
        /// </summary>
        IEventItem CreateIEventItem(IEventEdge data);
        /// <summary>
        /// Metoda, která má za úkol vytvořit celou plně definovanou cestu k nodu.
        /// Tato metoda se volá zcela výjimečně - tehdy, když tento objekt předepisuje nějakou TargetPath, která dosud neexistuje.
        /// Pak si jádro vyžádá plnou cestu k nodu, včetně textů a definice všech nodů.
        /// </summary>
        /// <param name="args">Data pro vytvoření cesty</param>
        /// <returns></returns>
        void CreateFullTreePath(TraceReaderCreatePathArgs args);
    }
    /// <summary>
    /// ITraceReaderWithAutoTimeShift : reader, který umí zajistit TimeShift pro svoje data.
    /// </summary>
    public interface ITraceReaderWithAutoTimeShift
    {
        /// <summary>
        /// Metoda určí TimeShift pro svoje události oproti událostem jiným.
        /// Metoda vrátí true, pokud daný soubor je již zpracován a nebude třeba se k němu vracet.
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        bool DetectTimeShift(TraceReaderDetectTimeShift args);
    }
    /// <summary>
    /// Základní třída pro argumenty posílané do ITraceReader
    /// </summary>
    public abstract class TraceReaderArgs
    {
        public TraceReaderArgs(MainInfo main, System.IO.FileInfo fileInfo)
        {
            this.Main = main;
            this.FileInfo = fileInfo;
            this.UserData = new UserData();
        }
        /// <summary>
        /// Přístup k logickým trace datům
        /// </summary>
        public MainInfo Main { get; private set; }
        /// <summary>
        /// Vstup: infromace o vstupním souboru
        /// </summary>
        public System.IO.FileInfo FileInfo { get; private set; }
        /// <summary>
        /// Vstup/výstup: datum trace souboru odvozené z názvu souboru, odpovídá datu TraceInfo.TraceDate
        /// </summary>
        public DateTime TraceDate { get; set; }
        /// <summary>
        /// Libovolná uživatelská data, jejich život jde od první detekce jména a obsahu souboru skrz všechny funkce (zpracování obsahu) 
        /// </summary>
        public UserData UserData { get; set; }
		/// <summary>
		/// Typ obsahu tohoto trace souboru.
		/// Reader při detekci určí typ dat (víceméně jde o konstantu odpovídající druhu dat),
		/// odsud se přenese do vznikajícího objektu TraceInfo, odsud pak do všech zakládaných NodeInfo.
		/// Následně se při on-demand načítání určí, pro jaký Node se data mají načítat, a načtou se data pouze z odpovídajících trace souborů.
        /// Platí, že v jednom řádku mohou být data pouze z jednoho druhu trace souboru (i když samozřejmě budou z více fyzických souborů, ale podobných = jeden TraceReader).
		/// </summary>
		public string TraceType { get; set; }
        /// <summary>
        /// Priorita času tohoto serveru. Implicitní = null, nelze určit.
        /// Priorita času řídí určování časových offsetů a určení řídícího serveru z hlediska přesného času.
        /// Nejvyšší prioritu 1 má mít SQL server.
        /// Prioritu 2 má mít aplikační server (pokud bude mít k dispozici data z SQL serveru, bude si řídit svoje hodinky podle něj).
        /// Prioritu 3 má mít klientský trace ("Server"), bude řídit svůj čas od prioritnějších míst.
        /// </summary>
        public Int32? ServerTimePriority { get; set; }
	}
    /// <summary>
    /// Data pro detekci jména serveru (a dalších hodnot z názvu souboru).
    /// </summary>
    public class TraceReaderCheckNameArgs : TraceReaderArgs
    {
        internal TraceReaderCheckNameArgs(MainInfo main, System.IO.FileInfo fileInfo)
            : base(main, fileInfo)
        {
        }
        /// <summary>
        /// Výstup: chci se podívat dovnitř
        /// </summary>
        public bool CheckContent { get; set; }
        /// <summary>
        /// Výstup: Server. 
        /// Obecně: jakákoli textová identifikace serveru.
        /// V rámci tohoto serveru se považují jednotlivé trace soubory za kontinuální, 
        /// a jejich položky (v trace souborech) mohou být spojovány do jedné události i když pocházejí z různých postupných souborů.
        /// Typicky: adresář + základ názvu souboru ( + pořadové číslo dne, pokud v další den budou události číslovány zase od 1).
        /// Postupné řazení souborů za sebou je dané datem počátku obsahu, který se detekuje v metodě CheckContent().
        /// </summary>
        public string ServerName { get; set; }
    }
    /// <summary>
    /// Data pro detekci obsahu souboru
    /// </summary>
    public class TraceReaderCheckContentArgs : TraceReaderArgs
    {
        internal TraceReaderCheckContentArgs(MainInfo main, System.IO.FileInfo fileInfo, System.IO.StreamReader fileStreamReader)
            : base(main, fileInfo)
        {
            this.FileStreamReader = fileStreamReader;
        }
        /// <summary>
        /// Vstupní soubor je otevřen zde. Nezavírat !!!
        /// Řádky lze načítat standardně metodou args.FileStreamReader.ReadLine().
        /// Není vhodné je načítat všechy !!!! Metoda pro detekci má skončit co nejrychleji !!!
        /// Typicky si přečte úvodní řádek, a rozhodne se.
        /// Pro určení času ContentTime (End) je typicky třeba načítat data z posledního řádku.
        /// Kdo ale hledá poslední řádek tak, že dělá "while !EndOfStream { ReadLine() }, ten je truhlík.
        /// Pro rychlé načtení posledního řádku ze souboru je připravena metoda:
        /// string lastLine = args.ReadLastLine();
        /// </summary>
        public System.IO.StreamReader FileStreamReader { get; private set; }
        /// <summary>
        /// Priorita, s jakou chce tento reader načítat tento soubor.
        /// 0 (a záporné) = nechci tento soubor zpracovávat.
        /// 1 jsou systémové readery.
        /// Vyšší hodnoty by měly vracet pluginy.
        /// Nejvyšší vrácená hodnota vyhrává a ten reader, kdo ji vrátil, bude tento soubor načítat.
        /// </summary>
        public decimal Priority { get; set; }
        /// <summary>
        /// Texty vypisované uvnitř elementu.
        /// Definuje se jich celá sada různých variant, lišících se v zásadě délkou textu.
        /// Graf vybere ten text, který se do elementu vejde vepsat (šířka elementu závisí na jeho velikosti a měřítku časové osy).
        /// </summary>
        public string[] ElementTexts { get; set; }
        /// <summary>
        /// Tooltip zobrazovaný v řádku elementu za tento soubor.
        /// Uplatní se jen tehdy, když žádný jiný reader pro tento souboru nebude mít větší prioritu.
        /// </summary>
        public string ElementTooltip { get; set; }
        /// <summary>
        /// Deklarace cest k root nodům, které bude tento trace soubor a reader plnit.
        /// Uplatní se jen tehdy, když žádný jiný reader pro tento souboru nebude mít větší prioritu.
        /// Reader může v průběhu zpracování souborů použít i další TreePath, ale ty budou do grafu přidány až při prohlížení.
        /// Zde definované TreePath budou svítit od začátku (je to přehlednější).
        /// </summary>
        public TreePath[] TreePaths { get; set; }
        /// <summary>
        /// Výstup: datum a čas prvního a posledního záznamu v souboru.
        /// Převezme se od readeru s nejvyšší prioritou.
        /// </summary>
        public TimeRange ContentTime { get; set; }
		/// <summary>
		/// Metoda načte a vrátí první řádek.
		/// Vystaví pointer na pozici 0, načte první řádek a odebere z něj případnou signaturu kódování.
		/// </summary>
		/// <returns></returns>
		public string ReadFirstLine()
		{
			System.IO.StreamReader fsr = this.FileStreamReader;

			// Zahodím buffery StreamReader, půjdu pro nová data natvrdo do souboru:
			fsr.DiscardBufferedData();
			fsr.BaseStream.Seek(0, System.IO.SeekOrigin.Begin);
			string line = fsr.ReadLine();

			if (fsr.EndOfStream)
				return "";

			// V prvním řádku se mohou na počátku vyskytovat znaky deklarující UTF8, projevují se jako char s kódem > 255, takové znaky odeberu (beztak v titulkovém řádku očekávám čisté ASCII znaky bez diakritiky):
			while (line.Length > 1 && (((int)line[0]) <= 32 || ((int)line[0]) >= 255))
				line = line.Substring(1);

			return line.Trim();
		}
		/// <summary>
        /// Metoda rychle načte a vrátí poslední řádek.
        /// Pokud v souboru je jen jeden jediný řádek, pak ho nevrátí !!!  Vrací null.
        /// Metoda nedokáže zjistit číslo tohoto řádku.
        /// Informace: při měření na souboru o velikosti 27 MB o 65000 řádkách jsou časy:
        /// a) čtení touto metodou = 0.350 milisekund
        /// b) hledání postupem "while !EndOfStream { line = ReadLine(); }    = 200 milisekund = 500x pomalejší. 
        /// Při počtu souborů v projektu například 30 je rozdíl časů: a) 10 milisekund,  b) 6 sekund. 
        /// A to je znát...
        /// </summary>
        /// <param name="notEmptyLine">Vyhledat neprázdný poslední řádek (true) / false = najít skutečně poslení, i když by byl prázdný</param>
        /// <returns></returns>
        public string ReadLastLine(bool notEmptyLine)
        {
            return _ReadLastLine(notEmptyLine, "", 0);
        }
		/// <summary>
        /// Metoda rychle načte a vrátí poslední řádek.
        /// Pokud v souboru je jen jeden jediný řádek, pak ho nevrátí !!!  Vrací null.
        /// Metoda nedokáže zjistit číslo tohoto řádku.
        /// Informace: při měření na souboru o velikosti 27 MB o 65000 řádkách jsou časy:
        /// a) čtení touto metodou = 0.350 milisekund
        /// b) hledání postupem "while !EndOfStream { line = ReadLine(); }    = 200 milisekund = 500x pomalejší. 
        /// Při počtu souborů v projektu například 30 je rozdíl časů: a) 10 milisekund,  b) 6 sekund. 
        /// A to je znát...
        /// </summary>
        /// <param name="delimiter">Oddělovač prvků, viz parametr minItemCount</param>
        /// <param name="minItemCount">Nejmenší počet prvků v řádku, abych vzal řádek jako korektní.
        /// Touto cestou lze zajistit ignorování posledního řádku, který obsahuje jen část textu (typicky uříznutý text v případě, kdy se kopíruje trace soubor za běhu, kdy konec trace ještě není uložen na disk)</param>
        /// <returns></returns>
        public string ReadLastLine(string delimiter, int minItemCount)
        {
            return _ReadLastLine(true, delimiter, minItemCount);
        }
		/// <summary>
        /// Metoda rychle načte a vrátí poslední řádek.
        /// Pokud v souboru je jen jeden jediný řádek, pak ho nevrátí !!!  Vrací null.
        /// Metoda nedokáže zjistit číslo tohoto řádku.
        /// Informace: při měření na souboru o velikosti 27 MB o 65000 řádkách jsou časy:
        /// a) čtení touto metodou = 0.350 milisekund
        /// b) hledání postupem "while !EndOfStream { line = ReadLine(); }    = 200 milisekund = 500x pomalejší. 
        /// Při počtu souborů v projektu například 30 je rozdíl časů: a) 10 milisekund,  b) 6 sekund. 
        /// A to je znát...
        /// </summary>
        /// <param name="notEmptyLine">Vyhledat neprázdný poslední řádek (true) / false = najít skutečně poslení, i když by byl prázdný</param>
        /// <param name="delimiter">Oddělovač prvků, viz parametr minItemCount</param>
        /// <param name="minItemCount">Nejmenší počet prvků v řádku, abych vzal řádek jako korektní.
        /// Touto cestou lze zajistit ignorování posledního řádku, který obsahuje jen část textu (typicky uříznutý text v případě, kdy se kopíruje trace soubor za běhu, kdy konec trace ještě není uložen na disk)</param>
        /// <returns></returns>
        private string _ReadLastLine(bool notEmptyLine, string delimiter, int minItemCount)
        {
            System.IO.StreamReader fsr = this.FileStreamReader;

            if (fsr.BaseStream == null)
                throw new InvalidOperationException("Nelze vyhledat poslední řádek souboru, podkladový Stream je neznámý.");
            if (!fsr.BaseStream.CanSeek)
                throw new InvalidOperationException("Nelze vyhledat poslední řádek souboru, podkladový Stream (typ: " + fsr.BaseStream.GetType().Name + ") nepodporuje funkci Seek.");

            long chunk = 1024L;
            long lenght = fsr.BaseStream.Length;
            long offset = lenght - chunk;
            if (offset < 0) offset = 0;

            while (true)
            {
                // Zahodím buffery StreamReader, půjdu pro nová data natvrdo do souboru:
                fsr.DiscardBufferedData();
                // Šoupnu pozici v souboru někam ke konci (offset ukazuje na pozici kousek před koncem souboru):
                fsr.BaseStream.Seek(offset, System.IO.SeekOrigin.Begin);
                // Přečtu část řádku z místa, kde zrovna stojím (pravděpodobně nestojíme na jeho začátku, takže jej zahodím):
                fsr.ReadLine();
                // A nyní už standardně hledám poslední řádek:
                string lastLine = null;
                while (!fsr.EndOfStream)
                {
                    string line = fsr.ReadLine();
                    if (_ReadLastLineIsOk(line,  notEmptyLine, delimiter, minItemCount))
                        // Daný řádek odpovídá pravidlům: zapamatujeme si jej (případně se přepíše následujícím řádkem, který rovněž bude odpovídat pravidlům):
                        lastLine = line;
                }
                if (lastLine != null)
                    return lastLine;

                // Pokud jsem zde, tak smyčka while (fsr.EndOfStream) lastLine = fsr.ReadLine(); nenačetla žádný řádek.
                // To je tehdy, když poslední řádek je delší, než jsme očekávali (viz offset, Seek(-offset)).
                //  Jinými slovy: přesunutím na daný offset jsme se přesunuli na řádek, který je posledním řádekm, za ním nic dalšího není, 
                //  ale přitom nemáme jistotu že tento poslední řádek skutečně začíná na daném offsetu (proto ten první načtený zahazujeme).
                // Zkusíme to znovu, ale offset posuneme směrem k začátku, tím př příštím chodu prohlédneme větší část konce souboru:
                if (offset == 0)
                    // Pokud už teď je offset na začátku, značí to že v souboru je jen jeden řádek (a proto nelze najít žádný řádek za prvním řádkem), pak nevracím nic:
                    return null;
                
                // Posunu se blíž k začátku:
                offset -= chunk;
                if (offset < 0)
                    offset = 0;
            }
        }
        /// <summary>
        /// Zjistí, zda daný řádek vyhovuje zadání
        /// </summary>
        /// <param name="line">Řádek</param>
        /// <param name="notEmptyLine">true=Pouze neprázdné řádky / false = i prázdný je OK</param>
        /// <param name="delimiter">Oddělovač prvků</param>
        /// <param name="minItemCount">Minimální počet prvků</param>
        /// <returns></returns>
        private bool _ReadLastLineIsOk(string line, bool notEmptyLine, string delimiter, int minItemCount)
        {
            if (!notEmptyLine) return true;                              // Řádek může být prázdný: pak je řádek OK bez dalších testů = true.
            if (line.IsEmpty()) return false;                            // Řádek musí být neprázdný. Pokud tento řádek je prázdný, pak false.
            if (minItemCount == 0 || delimiter.IsEmpty()) return true;   // Řádek je neprázdný. Pokud není zadaný počet prvků nebo oddělovač, pak je řádek OK = true.

            string[] items = line.Split(new string[] { delimiter }, StringSplitOptions.None);
            return (items.Length >= minItemCount);                       // Řádek musí obsahovat určitý minimální počet prvků. Vrátím true, pokud je tomu tak.
        }
	}
    /// <summary>
    /// Data pro načítání celého obsahu souboru
    /// </summary>
    public class TraceReaderProcessContentArgs : TraceReaderArgs
    {
        internal TraceReaderProcessContentArgs(MainInfo main, TraceInfo trace, System.IO.StreamReader fileStreamReader, ProcessContentMode processMode, Action<IEventEdge, TraceInfo> processItem, Action<IEventItem, TraceInfo> processEvent)
            : base(main, new System.IO.FileInfo(trace.FileInfo.FullName))
        {
            this.Trace = trace;
            this.TraceDate = trace.TraceDate;
			this.TraceType = trace.TraceType;
            this.UserData = trace.UserData;
            this.FileStreamReader = fileStreamReader;
			this.ProcessMode = processMode;
            this.ProcessItemAction = processItem;
            this.ProcessEventAction = processEvent;
        }
        /// <summary>
        /// Data o Trace souboru
        /// </summary>
        public TraceInfo Trace { get; private set; }
		/// <summary>
		/// Data o serveru, pro který se data načítají
		/// </summary>
		public ServerInfo Server { get { return this.Trace.Server; } }
        /// <summary>
        /// Vstupní soubor je otevřen zde. Nezavírat !!!
        /// Řádky lze načítat standardně metodou args.FileStreamReader.ReadLine().
        /// </summary>
        public System.IO.StreamReader FileStreamReader { get; private set; }
		/// <summary>
		/// Režim zpracovávání položek v metodě ITraceReader.ProcessContent().
		/// Standardně se data načtená z trace ukládají do systému.
		/// Ve výjimečných případech se ale neukládají, pouze se scanují (když některá funkce provádí scan dosud nenačtených dat).
		/// </summary>
		public ProcessContentMode ProcessMode { get; private set; }
		/// <summary>
		/// Delegát metody, která zajistí zpracování každé položky, která se do ní pošle.
		/// Reader si zpracovává data ze vstupního souboru, vytváří z nich položky typu ITraceItem,
		/// a každou položku předá do této metody, která zajistí její převzetí do systému.
		/// Pokud chce reader některá data vynechat, prostě je sem nepošle.
		/// Pokud chce některá data zpracovat do dvou položek, udělá to a pošle je dvakrát.
		/// </summary>
        protected Action<IEventEdge, TraceInfo> ProcessItemAction { get; private set; }
		/// <summary>
		/// Delegát metody, která zajistí zpracování každé události, která se do ní pošle.
		/// Reader může z trace souboru generovat rovnou eventy (ne itemy typu IEventEdge, ale eventy typu IEventItem).
		/// </summary>
        protected Action<IEventItem, TraceInfo> ProcessEventAction { get; private set; }
        /// <summary>
        /// Metoda, která zajistí zpracování každé položky, která se do ní pošle.
        /// Reader si zpracovává data ze vstupního souboru, vytváří z nich položky typu ITraceItem,
        /// a každou položku předá do této metody, která zajistí její převzetí do systému.
        /// Pokud chce reader některá data vynechat, prostě je sem nepošle.
        /// Pokud chce některá data zpracovat do dvou položek, udělá to a pošle je dvakrát.
        /// </summary>
        public void ProcessItem(IEventEdge eventEdge)
        {
            if (this.ProcessItemAction != null)
                this.ProcessItemAction(eventEdge, this.Trace);
        }
        /// <summary>
        /// Metoda, která zajistí zpracování každé události, která se do ní pošle.
        /// Reader může z trace souboru generovat rovnou eventy (ne itemy typu IEventEdge, ale eventy typu IEventItem).
        /// </summary>
        public void ProcessEvent(IEventItem eventItem)
        {
            if (this.ProcessEventAction != null)
                this.ProcessEventAction(eventItem, this.Trace);
        }
    }
    /// <summary>
    /// Data pro zpracování TimeShiftu
    /// </summary>
    public class TraceReaderDetectTimeShift : TraceReaderArgs
    {
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="main"></param>
        /// <param name="trace"></param>
        internal TraceReaderDetectTimeShift(MainInfo main, TraceInfo trace)
            : base(main, new System.IO.FileInfo(trace.FileInfo.FullName))
        {
            this.Trace = trace;
            this.TraceDate = trace.TraceDate;
			this.TraceType = trace.TraceType;
            this.UserData = trace.UserData;
        }
        /// <summary>
        /// Data o Trace souboru
        /// </summary>
        public TraceInfo Trace { get; private set; }
		/// <summary>
		/// Data o serveru, pro který se data načítají
		/// </summary>
		public ServerInfo Server { get { return this.Trace.Server; } }

    }
	/// <summary>
	/// Režim zpracovávání položek v metodě ITraceReader.ProcessContent()
	/// </summary>
	public enum ProcessContentMode
	{
		None = 0,
		/// <summary>
		/// Čtení položek a ukládání do systému
		/// </summary>
		ReadItems,
		/// <summary>
		/// Scanování položek, do systému se ukládat nebudou (v procesu analýzy dat).
        /// Vytvářené položky mají implementovat interface IEventEdge nebo IEventItem.
		/// </summary>
		ScanItems,
        /// <summary>
        ///  Scanování položek, do systému se ukládat nebudou (v procesu analýzy dat).
        ///  Položky by neměly ukládat ani svoje texty do StringHeap, načtená data by se měla zpracovávat minimálním způsobem.
        ///  Požaduje se načtení typu object, který reálně má být typu co nejbližšího nativnímu typu dat (například string[], XElement, atd.)
        /// </summary>
        ScanObjects
	}
    /// <summary>
    /// Data pro metodu, která sestavuje plnou definici cesty k nodu (včetně deklarace grafických vlastností řádku).
    /// </summary>
    public class TraceReaderCreatePathArgs
    {
        public TraceReaderCreatePathArgs(ProcessInfo processInfo, string path, IEventItem eventItem)
        {
            this.Process = processInfo;
            this.Path = path;
            this.EventItem = eventItem;
        }
        /// <summary>
        /// Data o serveru, pro který se data načítají
        /// </summary>
        public ProcessInfo Process { get; private set; }
        /// <summary>
        /// Data o serveru, pro který se data načítají
        /// </summary>
        public ServerInfo Server { get { return this.Process.Server; } }
        /// <summary>
        /// Přístup k logickým trace datům
        /// </summary>
        public MainInfo Main { get { return this.Process.Main; } }
        /// <summary>
        /// Data o události
        /// </summary>
        public IEventItem EventItem { get; private set; }
        /// <summary>
        /// Cesta, kterou je třeba plně sestavit
        /// </summary>
        public string Path { get; private set; }
        /// <summary>
        /// Výsledek = plně sestavená cesta
        /// </summary>
        public TreePath TreePath { get; set; }
    }
    #endregion
    #region ITraceScanner : specifikace rozšířených vlastností readeru, pokud chce podporovat rychlé scanování obsahu trace souborů bez ukládání do paměti (obchází i StringHeap)
    /// <summary>
    /// interface ITraceScanner : specifikace rozšířených vlastností readeru, pokud chce podporovat rychlé scanování obsahu trace souborů bez ukládání do paměti (obchází i StringHeap)
    /// </summary>
    public interface ITraceScanner
    {
        /// <summary>
        /// Metoda scanuje obsah trace souboru, a každou načtenou událost odesílá do akce args.ProcessLine().
        /// </summary>
        /// <param name="args"></param>
        void ScanContent(TraceReaderScanContentArgs args);
    }
    /// <summary>
    /// Data pro načítání celého obsahu souboru
    /// </summary>
    public class TraceReaderScanContentArgs : TraceReaderArgs
    {
        internal TraceReaderScanContentArgs(MainInfo main, TraceInfo trace, System.IO.StreamReader fileStreamReader, ProcessContentMode processMode, Action<object, TraceInfo> processObject)
            : base(main, new System.IO.FileInfo(trace.FileInfo.FullName))
        {
            this.Trace = trace;
            this.TraceDate = trace.TraceDate;
			this.TraceType = trace.TraceType;
            this.UserData = trace.UserData;
            this.FileStreamReader = fileStreamReader;
			this.ProcessMode = processMode;
            this.ProcessObjectAction = processObject;
        }
        /// <summary>
        /// Data o Trace souboru
        /// </summary>
        public TraceInfo Trace { get; private set; }
		/// <summary>
		/// Data o serveru, pro který se data načítají
		/// </summary>
		public ServerInfo Server { get { return this.Trace.Server; } }
        /// <summary>
        /// Vstupní soubor je otevřen zde. Nezavírat !!!
        /// Řádky lze načítat standardně metodou args.FileStreamReader.ReadLine().
        /// </summary>
        public System.IO.StreamReader FileStreamReader { get; private set; }
		/// <summary>
		/// Režim zpracovávání položek v metodě ITraceReader.ProcessContent().
		/// Standardně se data načtená z trace ukládají do systému.
		/// Ve výjimečných případech se ale neukládají, pouze se scanují (když některá funkce provádí scan dosud nenačtených dat).
		/// </summary>
		public ProcessContentMode ProcessMode { get; private set; }
		/// <summary>
		/// Delegát metody, která zajistí zpracování každé položky, která se do ní pošle.
		/// Reader si zpracovává data ze vstupního souboru, člení je na události (řádky), 
        /// a ty téměř beze změn předává do akce args.ProcessLine, která zajistí její převzetí do systému.
		/// Pokud chce reader některá data vynechat, prostě je sem nepošle.
		/// Pokud chce některá data zpracovat do dvou položek, udělá to a pošle je dvakrát.
		/// </summary>
        protected Action<object, TraceInfo> ProcessObjectAction { get; private set; }
        /// <summary>
        /// Metoda, která zajistí zpracování každé položky, která se do ní pošle.
        /// Reader si zpracovává data ze vstupního souboru, vytváří z nich položky co nejnižšího typu,
        /// a každou položku předá do této metody, která zajistí její převzetí do systému.
        /// Pokud chce reader některá data vynechat, prostě je sem nepošle.
        /// Pokud chce některá data zpracovat do dvou položek, udělá to a pošle je dvakrát.
        /// </summary>
        public void ProcessObject(object data)
        {
            if (this.ProcessObjectAction != null)
                this.ProcessObjectAction(data, this.Trace);
        }
    }
    #endregion
    #region ITraceInfoSupply : služba, která v trace datech najde doplňující informace k danému místu a času
    /// <summary>
    /// interface ITraceInfoSupply : služba, která v trace datech najde doplňující informace k danému místu a času
    /// </summary>
    public interface ITraceInfoSupply : IPlugin
    {
        /// <summary>
        /// Služba dostane informace o události, a doplní řadu dalších souvisejících informací do pole args.InfoList
        /// </summary>
        /// <param name="args"></param>
        void SupplyInfo(TraceInfoSupplyArgs args);
    }
    /// <summary>
    /// Data pro metodu SupplyInfo() pro interface ITraceInfoSupply.
    /// Metoda má pro událost EventInfo najít související informace a uložit je do pole InfoList
    /// </summary>
    public class TraceInfoSupplyArgs
    {
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="eventInfo"></param>
        public TraceInfoSupplyArgs(EventInfo eventInfo, string supplyRequest)
        {
            this.EventInfo = eventInfo;
            this.SupplyRequest = supplyRequest;
            this.SupplyData = new UserData();
            this.ResultInfoList = new List<TraceInfoSupplyItem>();
        }
        /// <summary>
        /// Datová základna projektu
        /// </summary>
        public MainInfo Main { get { return this.EventInfo.Main; } }
        /// <summary>
        /// Proces, do něhož patří událost EventInfo
        /// </summary>
        public ProcessInfo Process { get { return this.EventInfo.Process; } }
        /// <summary>
        /// Událost, k níž se mají doplnit informace
        /// </summary>
        public EventInfo EventInfo { get; private set; }
        /// <summary>
        /// Specifikace druhu požadovaných informací. Vyplňuje volající. Služby typu ITraceInfoSupply na tuto hodnotu reagují.
        /// </summary>
        public string SupplyRequest { get; private set; }
        /// <summary>
        /// Libovolná data, která předává žadatel o informace (volající) tomu, kdo data má a poskytuje je (volaný).
        /// Jaká data jsou zde uložena je věcí konkrétní situace.
        /// </summary>
        public UserData SupplyData { get; private set; }
        /// <summary>
        /// Pole pro uložení přidaných informací
        /// </summary>
        public List<TraceInfoSupplyItem> ResultInfoList { get; private set; }
        /// <summary>
        /// Servisní metoda: najde pluginy typu ITraceInfoSupply a požádá je o doplnění informací do argumentu.
        /// Tvorbu argumentu zajišťuje volající (ten, kdo informace chce).
        /// Vkládání dat zajišťují pluginy.
        /// Tato metoda je pouze službou mezi nimi.
        /// </summary>
        /// <param name="supplyArgs"></param>
        internal static void SupplyInfoCollect(TraceInfoSupplyArgs supplyArgs)
        {
            IEnumerable<ITraceInfoSupply> supplys = Steward.GetServiceObjectList<ITraceInfoSupply>(true);
            foreach (ITraceInfoSupply supply in supplys)
            {
                try
                {
                    supply.SupplyInfo(supplyArgs);
                }
                catch (Exception)
                { }
            }
        }

    }
    /// <summary>
    /// Jednotka informace získaná v metodě ITraceInfoSupply.SupplyInfo().
    /// Obsahuje nalezené informace.
    /// </summary>
    public class TraceInfoSupplyItem
    {
        public TraceInfoSupplyItem()
        {
            this.BackColor = Color.Empty;
        }
        /// <summary>
        /// Klíč skupiny
        /// </summary>
        public string GroupKey { get; set; }
        /// <summary>
        /// Text v titulku skupiny
        /// </summary>
        public string GroupHeader { get; set; }
        /// <summary>
        /// Klíč ikony
        /// </summary>
        public string ImageKey { get; set; }
        /// <summary>
        /// Název údaje
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// Hodnota
        /// </summary>
        public string Value { get; set; }
        /// <summary>
        /// Tooltip
        /// </summary>
        public string ToolTipText { get; set; }
        public Color BackColor { get; set; }
        public Color FontColor { get; set; }
        public Font Font { get; set; }
    }
    #endregion
    #region IEventPointer : specifikace vlastností, které určují cíl události (proces, event, node)
    /// <summary>
    /// IEventPointer : specifikace vlastností, které určují cíl události (proces, event, node)
	/// </summary>
	public interface IEventPointer
	{
		/// <summary>
		/// Klíč procesu. Nemůže být prázdný (pak se event nikam nezařadí; ledaže bychom povolili jeden empty proces - ale zatím ne).
		/// </summary>
		string ProcessKey { get; }
		/// <summary>
		/// Klíč události. Může být empty, pak ale nebude možno do této události přidávat další data o události (IEventEdge), 
		/// protože je nebude možno spárovat.
		/// </summary>
		string EventKey { get; }
        /// <summary>
        /// Pořadové číslo události v rámci trace souboru, 
        /// slouží k přesnému třídění pořadí událostí tehdy, když čas dvou sousedních událostí je zcela shodný.
        /// Ono za 1 milisekundu lze stihnout desítky věcí...
        /// </summary>
        int LineNumber { get; }
    }
	#endregion
    #region IEventEdge : data události z trace souboru, charakteru zlomku události (Begin, End)
    /// <summary>
    /// IEventEdge : data události z trace souboru, charakteru zlomku události (Begin, End).
    /// Tento objekt má krátkou životnost, vzniká při načítání dat z trace souboru, 
    /// slouží k určení cíle události a k jejímu postupnému plnění.
    /// Poté, kdy předá svoje data do události (IEventItem), zaniká.
    /// </summary>
    public interface IEventEdge : IEventPointer, IDisposable
    {
        /// <summary>
        /// Klíčové slovo pro filtraci událostí.
        /// Uživatel může ve vlastnostech projektu určit, jaké události chce načítat (specifikuje je jako soupis stringových konstant).
        /// Každá událost pak musí uvádět svoje klíčové slovo, které je při načítání dat porovnáváno se zadaným filtrem, a pouze vyhovující události se přebírají do systému.
        /// Reader události nefiltruje, to zajišťuje datový systém.
        /// Názvy položek filtru jsou uvedeny na třídě TraceDocument jako konstanty začínající slovem FILTER_*.
        /// Speciální význam mají hodnoty: 
        /// a) hodnota null nebo empty: událost bude zobrazena jen v případě, kdy uživatel zaškrtne VŠE
        /// b) hodnota * bude zobrazena vždy, i když by uživatel nic nepovolil
        /// </summary>
        string FilterKeyword { get; }
    }
    #endregion
    #region IEventItem : data události z trace souboru, charakter ucelené události.
    /// <summary>
    /// IEventItem : data události z trace souboru, charakter ucelené události.
    /// Tento objekt se v systému uchovává celou dobu (do reloadu dat, do Clear()), a předstvuje viditelný element.
    /// Je uložen v interní struktuře dat unitř procesu, na který sám ukazuje (ProcessInfo.Events.EventInfo.EventItem).
    /// Musí implementovat i IEventPointer a IDataElement, protože tento objekt může hrát roli podkladu pro vizualizaci.
	/// </summary>
    public interface IEventItem : IEventPointer, IDataElement, IDisposable
	{
        /// <summary>
        /// Klíč skupiny událostí, které lze mezi sebou mergovat.
        /// Mergování je činnost, k níž dochází po dokončení načítání dat z trace souborů.
        /// Jejím účelem je najít formálně izolované události, které ale logicky patří do jedné události.
        /// Typickým představitelem je událost typu Cowley, která se může skládat z 5 samostatných událostí (HetCowley, RunPart1, RunPart3, RunPart4, RunPart5),
        /// ale pro uživatele má význam ji zobrazit najednou = a navíc dojde ke spojení různých fragmentů dat (číslo a název cowley, číslo třídy, název typu, metoda).
        /// Události, které se nechtějí mergovat, zde vrací null (nebo empty string).
        /// Události které mergovat chtějí, zde vrací název své skupiny, mergování pak probíhá jen pro události této skupiny.
        /// Pokračování v popisu metody IEventItem.MergeWith()
        /// </summary>
        string MergeKey { get; }
        /// <summary>
        /// Klíč domácího nodu, kam patří události tohoto procesu.
        /// Nemusí být přítomen v každé události, ale v rámci jednoho procesu nesmí být zadány dva různé údaje.
        /// </summary>
        string TargetHomeNode { get; }
        /// <summary>
        /// Klíče (cesty) k cílovým nodům, kam se tato událost má zobrazit.
        /// </summary>
        string[] TargetPaths { get; }
		/// <summary>
		/// Plné definice cest k nodům, kde se má tento element zobrazit.
		/// Pokud bude null, pak se načtou klíče z TargetPaths, a plnou definici musí určit TraceReader.
		/// </summary>
		TreePath[] TreePaths { get; }
        /// <summary>
		/// Čas události
		/// </summary>
		TimeRange Time { get; }
		/// <summary>
		/// Vloží do sebe další data z položky trace souboru.
		/// Po vytvoření tohoto objektu neobsahuje vlastní data, jen klíče.
		/// Vlastní data se vkládají opakovaným voláním této metody (přinejmenším 1x).
		/// Vkládaná data mají shodnou hodnotu EventKey, jak tento objekt.
		/// Podle toho byl tento objekt vytvořen a poté při další položce dohledán.
		/// </summary>
		/// <param name="eventEdge"></param>
        void AddData(IEventEdge eventEdge, TraceInfo traceInfo);
		/// <summary>
		/// Zkusí, zda tato událost (this) může být spojena s událostí časově následující ze stejné mergovací skupiny.
		/// Pokud ano, pak se v této metodě musí prolnout data obou událostí do this, a musí se vrátit true.
		/// Pokud je mergovat nelze, vrací se false.
		/// </summary>
		/// <param name="nextEventItem">Časově následující událost</param>
		/// <returns></returns>
		bool MergeWith(IEventItem nextEventItem);
        /// <summary>
        /// Skupina barevnosti, umožní konfigurovat barvy pro jednotlivé skupiny událostí bez toho, aby tuto konfiguraci řešila datová vrstva.
        /// Ta jen nadefinuje logickou skupinu barvy, ale hodnotu určuje konfigurace.
        /// V konfiguraci může být uvedeno více barev pro jednu ColorGroup, pak se varianta konkrétní barvy určuje podle indexu ColorIndex.
        /// Zohlední se pouze tehdy, když je vygenerován element s barvou Empty.
        /// </summary>
		// string ColorGroup { get; }
        /// <summary>
        /// Varianta barevnosti, používá se společně s this.ColorGroup.
        /// Elementy, které mají shodnou property ColorGroup i ColorIndex budou mít shodnou barvu.
        /// Hodnota ColorIndex dovoluje variovat základní barvu definovanou pro skupinu ColorGroup.
        /// Implicitní hodnota je přebírána z this.LineNumber, po vložení jiné hodnoty je vracena tato explicitní hodnota.
        /// Je vhodné vkládat například číslo funkce, číslo třídy instance atd, pak všechny výskyty této funkce mají stejnou barvu.
        /// Zohlední se pouze tehdy, když je vygenerován element s barvou Empty.
        /// </summary>
        int ColorIndex { get; }
	}
	#endregion
    #region IColorDeclarator : předpis pro objekt, který chce deklarovat barvy pro elementy.
	/// <summary>
	/// IColorDeclarator : předpis pro objekt, který chce deklarovat barvy pro elementy.
	/// </summary>
	public interface IColorDeclarator : IPlugin
	{
		/// <summary>
		/// V této metodě může objekt deklarovat názvy barev a konkrétní barvy.
		/// </summary>
		/// <param name="args"></param>
		void ColorDeclare(ColorDeclaratorArgs args);
	}
	/// <summary>
	/// Data pro deklaraci barev.
    /// Barvy se deklarují v metodě void IColorDeclarator.ColorDeclare(ColorDeclaratorArgs args).
    /// Jednotlivé barvy se deklarují příkazem: args.Append("ColorGroupName", Color.barva [, další barvy]);
	/// </summary>
    public class ColorDeclaratorArgs
	{
		internal ColorDeclaratorArgs(ColorList colorList)
		{
			this.ColorList = colorList;
		}
		/// <summary>
		/// Seznam barev, do něhož má deklarátor přidat svoje barvy.
		/// Typicky používá metodu ColorList.Append(string key, string name, params Color[] colors)
		/// </summary>
		internal ColorList ColorList { get; private set; }
        /// <summary>
        /// Vkládá novou položku - bez barev.
        /// Pokud položka daného jména již existuje, sloučí jejich barvy.
        /// </summary>
        /// <param name="key">Klíč barvy</param>
        /// <param name="name">Uživatelské jméno, bude zobrazováno při editaci</param>
        public void Append(string key, string name)
        {
            this.ColorList.Append(key, name, Components.ColorList.AppendMode.Leave, Components.ColorList.AppendMode.Leave);
        }
        /// <summary>
        /// Vkládá novou položku - včetně barev.
        /// Pokud položka daného jména již existuje, sloučí jejich barvy.
        /// </summary>
        /// <param name="key">Klíč barvy</param>
        /// <param name="colors">Sada barev</param>
        public void Append(string key, params Color[] colors)
        {
            this.ColorList.Append(key, ColorItem.GetName(key), Components.ColorList.AppendMode.Leave, Components.ColorList.AppendMode.Leave, colors);
        }
        /// <summary>
        /// Vkládá novou položku - včetně barev.
        /// Pokud položka daného jména již existuje, sloučí jejich barvy.
        /// </summary>
        /// <param name="key">Klíč barvy</param>
        /// <param name="name">Uživatelské jméno, bude zobrazováno při editaci</param>
        /// <param name="colors">Sada barev</param>
        public void Append(string key, string name, params Color[] colors)
        {
            this.ColorList.Append(key, name, Components.ColorList.AppendMode.Leave, Components.ColorList.AppendMode.Leave, colors);
        }
	}
	#endregion
	#region IElementCreator : interface, který zaručuje že jakýkoli objekt je schopen vytvořit grafický element
	/// <summary>
	/// IElementCreator : interface, který zaručuje že jakýkoli objekt lze evidovat jako element na časové ose (na základě GIDu a času),
	/// a že objekt je schopen vytvořit grafický element. Jaká data pro to objekt použije, je jeho věc.
	/// </summary>
	public interface IElementCreator
	{
		/// <summary>
		/// Klíč elementu
		/// </summary>
		GID GId { get; }
		/// <summary>
		/// Čas elementu
		/// </summary>
		TimeRange Time { get; }
		/// <summary>
		/// Tento element je viditelný
		/// </summary>
		bool Visible { get; }
		/// <summary>
		/// Metoda, která vrátí grafický element
		/// </summary>
		/// <param name="rowGId"></param>
		/// <returns></returns>
		IDataElement CreateElement(GID rowGId);
	}
	#endregion
    #region TraceConstants : konstanty
    /// <summary>
    /// Konstanty
    /// </summary>
    public class TraceConstants
    {
        #region public
        /// <summary>
        /// Název požadavku - získání dat o deadlocku
        /// </summary>
        public const string TRACESUPPLY_INFO_DEADLOCK = "Deadlock";
        /// <summary>
        /// Skupina informací - záhlaví (login)
        /// </summary>
        public const string TRACESUPPLY_GROUPKEY_HEADER = PROC_GROUP_HEADER;
        #endregion
        #region internal
        internal const string LIST_CONTENT_PROCESS = "Process";
        internal const string LIST_CONTENT_RESOURCE = "Resource";
        internal const string PROC_GROUP_HEADER = "Header";
        internal const string PROC_GROUP_PROCESS = "Process";
        internal const string PROC_GROUP_OWNER = "Owner";
        internal const string PROC_GROUP_WAITER = "Waiter";
        internal const string PROC_GROUP_OTHER = "Other";
        #endregion
    }
    #endregion
}
